<template>
  <div id="swiperWrapper">
    <!-- 轮播图图片部分 -->
    <div
      class="swiper"
      ref="swiper"
      v-bind:style="swiperStyle"
      @touchstart="touchStart"
      @touchmove="touchMove"
      @touchend="touchEnd"
      @transitionend="transitionEnd"
    >
      <slot name="slide"></slot>
    </div>

    <!-- 轮播图导航部分 -->
    <div class="indicator" v-show="showIndicator">
      <div
        class="indi-item"
        :class="{ active: index === indicatorIndex - 1 }"
        v-for="(item, index) in slideCount"
        :key="index"
        @click="indicatorClick(index)"
      ></div>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'Swiper',

    data() {
      return {
        // 当前显示的是第几张图片
        currentIndex: 1,
        // 控制轮播图的坐标，控制轮播图的滚动动画
        swiperStyle: {},
        // 是否处于滚动状态
        scrolling: false,
        // 显示区域的宽度
        totalWidth: 0,
        // 轮播图中的slide个数
        slideCount: 0,
        // 是否显示轮播图导航
        showIndicator: true,
        // 是否开启轮播功能，默认开启
        enable: true
      }
    },

    props: {
      // 手指滑动距离的比例，超过这个比例显示下一张图片，低于这个比例回弹到原来的图片
      moveRatio: {
        type: Number,
        default: 0.25
      },
      // 轮播图滚动动画的速度
      animateDuration: {
        type: String,
        default: '0.3s'
      },
      // 轮播图自动切换的时间间隔
      interval: {
        type: Number,
        default: 3000
      },
      // 表示swiper中的图片是否已经加载完成
      imgLoaded: {
        type: Boolean,
        required: true,
        default: false
      }
    },

    watch: {
      // 监听imgLoaded属性，当swiper中的数据请求完成并重新渲染swiper后，进行DOM操作
      imgLoaded() {
        let swiperEl = this.$refs.swiper
        let slidesEl = swiperEl.children

        this.slideCount = slidesEl.length
        this.totalWidth = swiperEl.offsetWidth

        // 如果轮播图中只有一张图片，隐藏导航，禁用轮播功能
        if (slidesEl.length <= 1) {
          this.showIndicator = false
          this.enable = false
        }

        // 克隆轮播图的第一张和最后一张添加到DOM中，实现无缝滚动效果
        if (slidesEl.length > 1) {
          let cloneFrist = slidesEl[0].cloneNode(true)
          let cloneLast = slidesEl[slidesEl.length - 1].cloneNode(true)
          swiperEl.appendChild(cloneFrist)
          swiperEl.insertBefore(cloneLast, slidesEl[0])

          // 默认显示第一张图片
          this.setTransform(-this.totalWidth)
          // 开启定时器，实现自动轮播
          this.startTimer()
        }
      }
    },

    computed: {
      // 导航的index，根据这个index决定哪个导航点处于active状态
      indicatorIndex() {
        let index = 0
        if (this.currentIndex > this.slideCount) {
          index = 1
        } else if (this.currentIndex === 0) {
          index = this.slideCount
        } else {
          index = this.currentIndex
        }
        return index
      }
    },

    methods: {
      // 开启定时器，轮播图自动轮播
      startTimer() {
        this.timer = setInterval(() => {
          this.currentIndex++
          this.scrollContent(-this.currentIndex * this.totalWidth)
        }, this.interval)
      },

      // 点击导航点切换图片
      indicatorClick(index) {
        this.currentIndex = index + 1
        this.scrollContent(-this.currentIndex * this.totalWidth)
      },

      // 控制轮播图滚动动画的函数
      scrollContent(targetPosition) {
        this.scrolling = true
        this.$set(this.swiperStyle, 'transition', `all ${this.animateDuration}`)
        this.setTransform(targetPosition)
      },

      // 设置轮播图当前坐标的函数
      setTransform(position) {
        this.$set(this.swiperStyle, 'transform', `translateX(${position}px)`)
      },

      // 滚动动画结束
      transitionEnd() {
        this.scrolling = false
        // 滚动动画结束后，移除位移的动画效果，否则会影响手指拖动的操作
        this.swiperStyle.transition = ''
        if (this.currentIndex > this.slideCount) {
          this.currentIndex = 1
          this.setTransform(-this.totalWidth)
        } else if (this.currentIndex <= 0) {
          this.currentIndex = this.slideCount
          this.setTransform(-this.currentIndex * this.totalWidth)
        }
      },

      // 手指触碰屏幕，记录初始坐标点
      touchStart(event) {
        clearInterval(this.timer)
        this.startX = event.touches[0].pageX
      },

      // 轮播图跟随手指滑动
      touchMove(event) {
        if (this.scrolling === true || this.enable === false) {
          return
        }
        let currentX = event.touches[0].pageX
        let distance = currentX - this.startX
        let currentPosition = -this.currentIndex * this.totalWidth + distance
        this.setTransform(currentPosition)
      },

      // 手指离开屏幕，根据滑动的距离决定滚动到上一张还是下一张
      touchEnd(event) {
        if (this.enable === false) {
          return
        }
        this.startTimer()
        let distance = event.changedTouches[0].pageX - this.startX
        if (distance === 0) {
          return
        }
        if (Math.abs(distance) > this.totalWidth * this.moveRatio) {
          distance > 0 && this.currentIndex--
          distance < 0 && this.currentIndex++
        }
        this.scrollContent(-this.currentIndex * this.totalWidth)
      }
    }
  }
</script>

<style lang="less" scoped>
  #swiperWrapper {
    position: relative;
    overflow: hidden;
  }
  .swiper {
    display: flex;
  }
  .indicator {
    display: flex;
    position: absolute;
    bottom: 8px;
    width: 100%;
    justify-content: center;
  }
  .indi-item {
    width: 8px;
    height: 8px;
    margin: 0 5px;
    background-color: #fff;
    border-radius: 4px;
  }
  .active {
    background-color: red;
  }
</style>
